﻿using System;
using System.Net.Sockets;
using MessageSystem.Default.Interface;
using MessageSystem.Default.infrastructure;
using MessageSystem.Managed.Interface;
using MessageSystem.Managed.Infrastructure;

namespace MessageSystem.Default.implement
{
    public unsafe class MessageChannel:MessageChannelBase,IMessageChannel
    {
        public MessageChannel(Socket sock, Selector pSelector, IMessageChannelFactory pFactory)
        {
            m_socket = sock;
            m_socket.Blocking = false;
            m_socket.SendBufferSize = UInt16.MaxValue;
            m_socket.ReceiveBufferSize = UInt16.MaxValue;
            m_pSelector = pSelector;
            m_pFactory = pFactory;
            m_bLost = false;
            m_sndlist = new list_msg();
            m_ns = new NetStream();
        }

        public void Open(IMessageHandler handler)
        {
            m_pHander = handler;
            m_pBlock = allocator.Instance.alloc_block();
            m_pRecvBufferPoint = m_pBlock.Point;
            m_pRecvBuffer = m_pBlock.buffer;
            m_nRecvBufferSize = m_pBlock.buffer.Length;
            m_nRecvSize = 0;
        }
        
        public bool Send(IMessage m, ushort nFlags, int nSessionId,int OpCode)
        {
            NetStream stream = BufferManager.Instance.OutputSteam.Stream;
            stream.Reset();
            m.Write(stream);

            if ((nFlags & (int)MessageFlags.Encryption) == (int)MessageFlags.Encryption)
            {
                XOREncryption.Encrypt(stream.DataPoint, (int)stream.WriteLength);
            }

            Send(stream.DataPoint, (int)stream.WriteLength, nFlags, m.MessageId, nSessionId, OpCode);
            return true;
        }

        private void Send(byte* pBody, int nBodyLength, ushort nFlags, int nMessageId, int nSessionId,int OpCode)
        {
            ushort totalsize = (ushort)(Message.MessageHead.Size + nBodyLength);
            msg pMsg = allocator.Instance.alloc_sized_msg(totalsize);
            Message.MessageHead* pHead = (Message.MessageHead*)pMsg.baseoffset();
            pHead->TotalLength = totalsize;
            pHead->Flags = nFlags;
            pHead->MessageId = nMessageId;
            pHead->SessionId = nSessionId;
            pHead->OpCode = OpCode;
            Copy(pBody, pMsg.baseoffset() + Message.MessageHead.Size, nBodyLength);
            pMsg.setlen(totalsize);
            m_sndlist.add_tail(pMsg);

            Flush();
        }

        public void Close()
        {
            m_socket.Shutdown(SocketShutdown.Both);
        }
        
        protected override void OnMsg(ref Message pMsg)
        {
            OnMsg(pMsg.Body, (UInt16)pMsg.BodyLength, pMsg.Head.Flags, pMsg.Head.MessageId, pMsg.Head.SessionId,pMsg.Head.OpCode);
        }

        private void OnMsg(byte* pBase, UInt16 length, UInt16 nFlags, Int32 nMessageId, Int32 nSessionId,Int32 OpCode)
        {
            if ((nFlags & (int)MessageFlags.Encryption) == (int)MessageFlags.Encryption)
            {
                XOREncryption.Encrypt(pBase, (int)length);
                nFlags &= 0xfffe;
            }

            m_ns.Reset(pBase, length);
            m_pHander.OnMsg(nFlags, nMessageId, nSessionId,OpCode,m_ns);
        }

        public bool Dispatch(ushort nFlags, int nMessageId, int nSessionId,Int32 OpCode,NetStream ns, int offset, int length)
        {
            int maxoffset = (int)ns.DataLength - length;
            if (offset < maxoffset)
            {
                Send((byte*)(&ns.DataPoint[offset]), (UInt16)length, nFlags, nMessageId, nSessionId,OpCode);
                return true;
            }
            else
            {
                return false;
            }
        }

        public void Poll()
        {
            if (m_bLost)
                return;

            if (m_pSelector.CanRead(m_socket))
            {
                Recv();
            }

            if (m_bLost)
                return;

            m_bWritable = m_pSelector.CanWrite(m_socket);
            Flush();
        }

        private void Copy(byte* src,byte* dst,int length)
        {
            while(length-->0)
            {
                *dst++ = *src++;
            }
        }

        protected override void OnLost()
        {
            m_bLost = true;
            m_pHander.OnLost();
            m_pHander = null;
            m_pFactory.Destroy(this);
            m_pSelector.UnRegister(m_socket);
            allocator.Instance.free_block(m_pBlock);
            m_socket.Close();
        }

        protected override void OnInternalError(int error)
        {
            Close();
        }

        private Selector m_pSelector;
        private IMessageHandler m_pHander;
        private IMessageChannelFactory m_pFactory;
        private bool m_bLost;
        private block m_pBlock;
        private NetStream m_ns;
    }
}
